// deps: SpriteDirection Util Point2d

// assume: for a given direction, delta is the same for each frame
// assume: for a given sprite the width of each frame is the same

var SpriteFactory = {
	fromBean: function( bean ) {
		var sprite = new Sprite();
		sprite.setImage( bean.image );
		sprite.setNode( Util.id( bean.id ) );
		sprite.addDirections( SpriteDirectionFactory.fromBeans( bean.directions ) );
		sprite.setOffset( Point2dFactory.fromBean( bean.offset ) );
		if ( bean.w ) sprite.setW( bean.w );
		if ( bean.h ) sprite.setH( bean.h );
		return sprite;
	}
	, fromBeans: function( beans ) {
		return Util.each( 
			beans
			, function( bean ) { return SpriteFactory.fromBean( bean ); }
		);
	}
};

var Sprite = function() {

	this.setImage = function( image ) {
		this.image_ = image;
	};

	this.getImage = function() {
		return this.image_;
	};

	this.setNode = function( node ) {
		this.node_ = node;
	};

	this.getNode = function() {
		return this.node_;
	};

	this.setPosition = function( position ) {
		this.position_ = position;
	};

	this.getPosition = function() {
		return this.position_;
	};

	this.setOffset = function( offset ) {
		this.offset_ = offset;
	};

	this.getOffset = function() {
		return this.offset_;
	};

	this.setW = function( w ) {
		this.w_ = w;
	};

	this.getW = function() {
		return this.w_;
	};

	this.setH = function( h ) {
		this.h_ = h;
	};

	this.getH = function() {
		return this.h_;
	};

	this.setDirections = function( directions ) {
		this.directions_ = directions;
	};

	this.getDirections = function() {
		return this.directions_;
	};

	this.addDirections = function( directions ) {
		var thiz = this;
		Util.each( directions, function( d ) { thiz.addDirection( d ); } );
	};

	this.addDirection = function( direction ) {
		this.getDirections()[ direction.getName() ] = direction;
	}

	this.setCurrentDirection = function( currentDirection ) {
		this.currentDirection_ = currentDirection;
	};

	this.getCurrentDirection = function() {
		return this.currentDirection_;
	};

	this.setCurrentDirectionByName = function( name ) {
		this.setCurrentDirection( this.getDirections()[ name ] );
	};

	this.to = function( name ) {
		this.setCurrentDirectionByName( name );
	};

	this.towardsPosition = function( pos ) {
		this.towards( new Point2d( pos.x, pos.y ) );
	};

	/* please do not confuse this for intelligence :-P */
	this.towards = function( p ) {
		var diff = new Point2d( 
			  p.getX() - this.getPosition().getX() 
			, p.getY() - this.getPosition().getY()
		);

		var min = 0;
		var best = '';
		for ( var dir in this.getDirections() ) {
			var dist = diff.distanceSquared( 
				this.getDirections()[ dir ].getDelta() 
			);
			if ( !best || dist < min ) {
				min = dist;
				best = dir;
			}
		}
		
		if ( this.willRoboShake( p, dir ) ) {
			this.setCurrentDirection( false );
		} else {
			this.to( best );
		}

		return this.getCurrentDirection();
	};

	// this prevents robo-shakes...
	this.willRoboShake = function( p, dir ) {
		var delta = this.getDirections()[ dir ].getDelta();
		var dist = ( delta.getX() * delta.getX() + delta.getY() * delta.getY() );
		var toGo = p.distanceSquared( this.getPosition() );
		return  ( dist > toGo );
	};

	this.moveTo = function( x, y ) {
		this.setPosition( new Point2d( x, y ) );
		this.moveNodeTo( x, y );
	};

	this.moveNodeTo = function( x, y ) {
		Util.moveTo( this.getNode(), x, y );
	};

	this.draw = function( map ) {
		if ( map ) {
			var mapped = map.mapToScreen( this.getPosition() );
			this.moveNodeTo( mapped.getX() , mapped.getY() );
		} else {
			var x = this.getPosition().getX() + ( this.getOffset() ? this.getOffset().getX() : 0 );
			var y = this.getPosition().getY() + ( this.getOffset() ? this.getOffset().getY() : 0 );
			this.moveNodeTo( x, y );
		}
		x = 0;
		y = 0;
		if ( this.getCurrentDirection() ) {
			x = this.getCurrentDirection().getCurrentFrame().getX();
			y = this.getCurrentDirection().getCurrentFrame().getY();
		}
		Util.cssSprite( 
			this.getNode()
			, this.getImage()
			, this.getW()
			, this.getH()
			, x 
			, y 
		);
		return this; /* just for fun, return yourself... :-D */
	};

	this.move = function( map ) {
		if( this.getCurrentDirection() ) {
			this.draw( map );
			this.getPosition().add( this.getCurrentDirection().getDelta() );
			this.getNode().style.zIndex = 1 + this.getPosition().getY(); 
			this.getCurrentDirection().move();
		}
	};

	this.init = function() {
		this.setDirections( [] );
		this.setPosition( new Point2d( 0 , 0 ) );
		this.setW( 32 );
		this.setH( 32 );
	};

	this.init();
};
